package fr.afcepf.atod.framework.ioc;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import fr.afcepf.atod.framework.exceptions.FrameworkException;
import fr.afcepf.atod.framework.properties.PropertiesManager;

public class IOCManagerImpl implements IOCManager {

	private Document appContextXml;
	private Map<String, Object> beans = new HashMap<String, Object>();

	private static final String DEFAULT_SCOPE = "singleton";

	private static Logger m_log = Logger.getLogger(IOCManagerImpl.class);

	@Override
	public void getConf() {
		Properties properties = PropertiesManager.loadPropertiesFile();
		if (!properties.containsKey("configurationType")) {
			m_log.error("configurationType non fourni");
		} else {
			if (properties.get("configurationType").equals("xml")) {
				m_log.info("Configuration de type XML");
				URL url = Thread
						.currentThread()
						.getContextClassLoader()
						.getResource(
								properties.getProperty("configurationFile"));
				File fic = new File(url.getFile());
				m_log.info("Fichier de conf : " + fic);
				appContextXml = loadXmlFile(fic);
//				loadBeans();
			}
		}

	}

	@Override
	public Document loadXmlFile(File fileName) {
		Document document = null;
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			document = db.parse(fileName);
			m_log.info("Parsing du fichier xml de conf");
		} catch (SAXException e) {
			m_log.error(e.getMessage());
		} catch (IOException e) {
			m_log.error(e.getMessage());
		} catch (ParserConfigurationException e) {
			m_log.error(e.getMessage());
		}
		return document;
	}

	@Override
	public Object getBean(String id) {
		// Map<String, String> result = new HashMap<String, String>();
		// NodeList nodelist = appContextXml.getElementsByTagName("bean");
		//
		// for (int i = 0; i < nodelist.getLength(); i++) {
		// Node object = nodelist.item(i);
		// NamedNodeMap nodeMap = object.getAttributes();
		//
		// if (nodeMap.getNamedItem("id").getNodeValue().equals(id)) {
		// m_log.info("<bean id=" + id + "> trouvé");
		// result.put("class", nodeMap.getNamedItem("class")
		// .getNodeValue());
		// result.put("id", nodeMap.getNamedItem("id").getNodeValue());
		// if (nodeMap.getNamedItem("scope")!=null)
		// result.put("scope", nodeMap.getNamedItem("scope")
		// .getNodeValue());
		//
		// }String
		// if (object.hasChildNodes()) {
		// NodeList children = object.getChildNodes();
		// for (int j = 0; j < children.getLength(); j++) {
		// Node child = children.item(j);
		// if (child.getNodeName().equals("property")) {
		// NamedNodeMap childAttributes = child.getAttributes();
		//
		// }
		// }
		// }
		//
		// for (int j = 0; j < nodelist.item(i).getAttributes().getLength();
		// j++) {
		// Node node = nodelist.item(i).getAttributes().item(j);
		// m_log.info(node.getNodeValue());
		// }
		// }
		// m_log.info("A la fin la Map contient : " + result);
		Object bean = null;
		Element elementBean = null;
		NodeList beanList = null;

		try {
			beanList = appContextXml.getElementsByTagName("bean");
			for (int i = 0; i < beanList.getLength(); i++) {
				Node currentNode = beanList.item(i);
				NamedNodeMap attributes = currentNode.getAttributes();
				if (id.equals(attributes.getNamedItem("id").getNodeValue())) {
					elementBean = (Element) currentNode;
				}

				if (elementBean == null) {
					throw new FrameworkException("erreur parsing du bean");
				}

				bean = createObject(elementBean, beans);

			}
		} catch (FrameworkException e) {
			m_log.error(e.getMessage());
		}

		return bean;
	}

	private Object createObject(Element elementBean, Map<String, Object> beans2) {
		String id = null;
		String className = null;
		String scope = DEFAULT_SCOPE;
		Class<?> beanClass = null;
		Object resultObject = null;
		NodeList properties = null;

		try {
			NamedNodeMap attributes = elementBean.getAttributes();
			id = attributes.getNamedItem("id").getNodeValue();
			if (attributes.getNamedItem("class") != null) {
				className = attributes.getNamedItem("class").getNodeValue();
			} else {
				throw new FrameworkException("attribut class manquant");
			}
			if (attributes.getNamedItem("scope") != null) {
				scope = attributes.getNamedItem("scope").getNodeValue();
			}
			if (scope.equals("prototype")) {

				beanClass = Class.forName(className);

				resultObject = beanClass.newInstance();

				properties = elementBean.getElementsByTagName("property");
				for (int i = 0; i < properties.getLength(); i++) {
					NamedNodeMap currentAttributes = properties.item(i)
							.getAttributes();
					generateProperty(id, beanClass, resultObject,
							currentAttributes);
				}
			} else {
				if (beans.containsKey(className)) {
					resultObject = beans.get(className);
				} else {
					beanClass = Class.forName(className);

					resultObject = beanClass.newInstance();

					properties = elementBean.getElementsByTagName("property");
					for (int i = 0; i < properties.getLength(); i++) {
						NamedNodeMap currentAttributes = properties.item(i)
								.getAttributes();
						generateProperty(id, beanClass, resultObject,
								currentAttributes);
					}
					beans.put(className, resultObject);
				}
			}

		} catch (ClassNotFoundException e) {
			m_log.error("Erreur creation Objet : " + e.getMessage());
		} catch (InstantiationException e) {
			m_log.error(e.getMessage());
		} catch (IllegalAccessException e) {
			m_log.error(e.getMessage());
		} catch (FrameworkException e) {
			m_log.error(e.getMessage());
		} catch (NoSuchFieldException e) {
			m_log.error(e.getMessage());
		} catch (SecurityException e) {
			m_log.error(e.getMessage());
		} catch (DOMException e) {
			m_log.error(e.getMessage());
		} catch (NoSuchMethodException e) {
			m_log.error(e.getMessage());
		} catch (InvocationTargetException e) {
			m_log.error(e.getMessage());
		}
		return resultObject;
	}

	private void generateProperty(String idBean, Class<?> elementClass,
			Object object, NamedNodeMap currentAttributes)
			throws FrameworkException, NoSuchFieldException,
			IllegalAccessException, NoSuchMethodException,
			ClassNotFoundException, InvocationTargetException {

		String fieldName = null;
		Field field = null;
		Class<?> generatedField = null;
		Object resultObject = null;

		if (currentAttributes.getNamedItem("name") == null) {
			throw new FrameworkException(
					"Attribut name de la property manquant pour le bean "
							+ idBean);
		}

		fieldName = currentAttributes.getNamedItem("name").getNodeValue();
		field = elementClass.getDeclaredField(fieldName);
		field.setAccessible(true);
		generatedField = field.getType();

		if (currentAttributes.getNamedItem("ref") != null) {
			resultObject = getBean(currentAttributes.getNamedItem("ref")
					.getNodeValue());
		} else {
			if (generatedField.isPrimitive()) {
				String parseType = generatedField.toString().substring(0, 1)
						.toUpperCase()
						+ generatedField.toString().substring(1);
				String parseName = "parse" + parseType;

				Method parseM = Class.forName("java.lang." + parseType)
						.getMethod(parseName, String.class);

				resultObject = parseM.invoke(generatedField, currentAttributes
						.getNamedItem("value").getNodeValue());
			} else {
				resultObject = currentAttributes.getNamedItem("value")
						.getNodeValue();
			}
		}
		field.set(object, resultObject);

	}

	public void loadBeans() {
		NodeList beanList = appContextXml.getElementsByTagName("bean");
		for (int i = 0; i < beanList.getLength(); i++) {
			m_log.info("Lecture du <bean id="
					+ beanList.item(i).getAttributes().getNamedItem("id")
							.getNodeValue() + ">");
			getBean(beanList.item(i).getAttributes().getNamedItem("id")
					.getNodeValue());
		}
		NodeList propertyList = appContextXml.getElementsByTagName("property");
		for (int i = 0; i < propertyList.getLength(); i++) {

		}
	}
}